package com.clp.protocol.iec104.server.pipeline.state.data;

import com.clp.protocol.iec104.apdu.asdu.infoobj.CInfoObj;
import com.clp.protocol.iec104.apdu.asdu.infoobj.infoelem.M_DP_NA_1_InfoElem;
import com.clp.protocol.iec104.apdu.asdu.infoobj.infoelem.M_ME_NC_1_InfoElem;
import com.clp.protocol.iec104.apdu.asdu.infoobj.infoelem.M_SP_NA_1_InfoElem;
import com.clp.protocol.iec104.apdu.asdu.infoobj.qua.M_ME_NC_1_Qua;
import com.clp.protocol.iec104.definition.ConstVal;
import com.clp.protocol.iec104.definition.Tm;
import com.clp.protocol.iec104.definition.TypeTag;
import com.clp.protocol.iec104.server.SlaveChannel;
import lombok.Getter;

import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;

/**
 * 总召唤的数据反应器
 */
@Getter
public abstract class TotalCall100DataReactor extends DataReactor {
    private final TmDataType tmDataType;
    private final TsDataType tsDataType;

    public TotalCall100DataReactor(Executor executor, TmDataType tmDataType, TsDataType tsDataType) {
        super(executor);
        this.tmDataType = tmDataType;
        this.tsDataType = tsDataType;
    }

    /**
     * 该连接通道 是否接受总召唤
     * @param slaveChannel
     * @return
     */
    public CompletableFuture<Boolean> accept(SlaveChannel slaveChannel) {
        return supplyAsync(() -> accept0(slaveChannel));
    }

    /**
     * 是否接受总召唤
     * @param slaveChannel
     * @return
     */
    protected abstract boolean accept0(SlaveChannel slaveChannel);

    public CompletableFuture<List<TmData>> collectTmData(SlaveChannel slaveChannel) {
        return supplyAsync(() -> collectTmData0(slaveChannel));
    }

    protected abstract List<TmData> collectTmData0(SlaveChannel slaveChannel);

    public CompletableFuture<List<TsData>> collectTsData(SlaveChannel slaveChannel) {
        return supplyAsync(() -> collectTsData0(slaveChannel));
    }

    protected abstract List<TsData> collectTsData0(SlaveChannel slaveChannel);

    /**
     * 总召唤的遥测类型
     */
    @Getter
    public enum TmDataType {
        /**
         * 短浮点数
         */
        FLOAT(TypeTag.M_ME_NC_1) {
            @Override
            public CInfoObj convert(TmData tmData) {
                M_ME_NC_1_InfoElem infoElem = new M_ME_NC_1_InfoElem(tmData.getValue().floatValue());
                M_ME_NC_1_Qua qua = new M_ME_NC_1_Qua(Tm.Valid.gain(tmData.isValid()));
                return new CInfoObj(getTypeTag(), tmData.getAddress(), infoElem, qua, null);
            }

            @Override
            public CInfoObj convertInvalid(int addr) {
                M_ME_NC_1_InfoElem infoElem = new M_ME_NC_1_InfoElem(0f);
                M_ME_NC_1_Qua qua = new M_ME_NC_1_Qua(Tm.Valid.NOT_VALID);
                return new CInfoObj(getTypeTag(), addr, infoElem, qua, null);
            }
        };

        private final TypeTag typeTag;
        private final int maxCount;

        TmDataType(TypeTag typeTag) {
            this.typeTag = typeTag;
            this.maxCount = (ConstVal.APDU_VARIABLE_SIZE - ConstVal.INFO_ELEM_ADDRESS_LEN) /
                    (typeTag.getInfoElemBytesLen() + ConstVal.VSQ_LEN);
        }

        /**
         * 转换为信息元素
         * @param tmData
         * @return
         */
        public abstract CInfoObj convert(TmData tmData);

        public abstract CInfoObj convertInvalid(int addr);
    }

    /**
     * 总召唤的遥信类型
     */
    @Getter
    public enum TsDataType {
        /**
         * 单点遥信
         */
        ONE_POINT(TypeTag.M_SP_NA_1){
            @Override
            public CInfoObj convert(TsData data) {
                M_SP_NA_1_InfoElem infoElem = new M_SP_NA_1_InfoElem(data.isValid(), data.isCurrVal(), data.isReplaced(), data.isLocked(), data.isSwitchOn());
                return new CInfoObj(getTypeTag(), data.getAddress(), infoElem, null, null);
            }

            @Override
            public CInfoObj convertInvalid(int addr) {
                M_SP_NA_1_InfoElem infoElem = new M_SP_NA_1_InfoElem(false, true, false, false, false);
                return new CInfoObj(getTypeTag(), addr, infoElem, null, null);
            }
        },
        /**
         * 双点遥信
         */
        TWO_POINT(TypeTag.M_DP_NA_1) {
            @Override
            public CInfoObj convert(TsData data) {
                M_DP_NA_1_InfoElem infoELem = new M_DP_NA_1_InfoElem(data.isValid(), data.isCurrVal(), data.isReplaced(), data.isLocked(), data.isSwitchOn());
                return new CInfoObj(getTypeTag(), data.getAddress(), infoELem, null, null);
            }

            @Override
            public CInfoObj convertInvalid(int addr) {
                M_DP_NA_1_InfoElem infoELem = new M_DP_NA_1_InfoElem(false, true, false, false, false);
                return new CInfoObj(getTypeTag(), addr, infoELem, null, null);
            }
        };

        private final TypeTag typeTag;
        private final int maxCount;

        TsDataType(TypeTag typeTag) {
            this.typeTag = typeTag;
            this.maxCount = (ConstVal.APDU_VARIABLE_SIZE - ConstVal.INFO_ELEM_ADDRESS_LEN) / (typeTag.getInfoElemBytesLen());
        }

        public abstract CInfoObj convert(TsData data);

        public abstract CInfoObj convertInvalid(int addr);
    }
}
